
package multiNyID.Detector;
/*
 * class MarkerDetectorNyID
 *
 * MarkerDetectorNyID --> kelas untuk melakukan deteksi marker pada tiap frame yang ditangkap kamera
 * NyARDetectMarkerResultStack --> kelas berupa stack untuk menampung hasil deteksi marker pada suatu frame
 * NyARDetectMarkerResult --> kelas berupa konten informasi yang disimpan dalan Stack
 *                        |-> NyID : NyID marker yang terdeteksi , square : data poosisi marker
 * --------------------------------------------------------------------------------
 * Based On NyARToolkit The Java edition ARToolKit class library.
 * modified from class SingleNyIdMarkerProcesser
 *
 * @author whe_green@yahoo.co.id
 * Analisis Metode Occlusion Based Pada Augmented Reality
 * Studi Kasus Interaksi dengan Objek Virtual Secara Realtime
 * Menggunakan Gerakan Tangan
 *
 * Institut Teknologi Telkom
 * Fakultas Informatika 2007
 * Bandung Indonesia
 *
 * nyargreen.wordpress.com
 */
import jp.nyatla.nyartoolkit.NyARException;
import jp.nyatla.nyartoolkit.core.analyzer.raster.threshold.*;
import jp.nyatla.nyartoolkit.core.param.*;
import jp.nyatla.nyartoolkit.core.raster.*;
import jp.nyatla.nyartoolkit.core.raster.rgb.*;
import jp.nyatla.nyartoolkit.core.transmat.*;
import jp.nyatla.nyartoolkit.core.rasterfilter.rgb2bin.NyARRasterFilter_ARToolkitThreshold;
import jp.nyatla.nyartoolkit.core.types.*;
import jp.nyatla.nyartoolkit.nyidmarker.*;
import jp.nyatla.nyartoolkit.nyidmarker.data.*;
import jp.nyatla.nyartoolkit.core.squaredetect.*;
import jp.nyatla.nyartoolkit.core.types.stack.NyARObjectStack;


class NyARDetectMarkerResult
{

        public int NyID;
	public NyARSquare square=new NyARSquare();
}


class NyARDetectMarkerResultStack extends NyARObjectStack<NyARDetectMarkerResult>
{
	public NyARDetectMarkerResultStack(int i_length)throws NyARException
	{
		super(i_length,NyARDetectMarkerResult.class);
		return;
	}
	protected NyARDetectMarkerResult createElement()
	{
		return new NyARDetectMarkerResult();
	}
}

public class MarkerDetectorNyID
{
	/**
	 * detectMarker callback
	 */
	private class DetectSquareCB implements NyARSquareContourDetector.IDetectMarkerCallback
	{

                //stack
                public NyARDetectMarkerResultStack result_stack = new NyARDetectMarkerResultStack(MarkerDetectorNyID.max);

		//reference
		private INyARRgbRaster _ref_raster;

		private INyIdMarkerData _current_data;
		private final NyIdMarkerPickup _id_pickup = new NyIdMarkerPickup();
		private NyARCoord2Linear _coordline;
		private INyIdMarkerDataEncoder _encoder;

		private INyIdMarkerData _data_temp;
		private INyIdMarkerData _prev_data;
		
		public DetectSquareCB(NyARParam i_param,INyIdMarkerDataEncoder i_encoder)throws NyARException
		{
			this._coordline=new NyARCoord2Linear(i_param.getScreenSize(),i_param.getDistortionFactor());
			this._data_temp=i_encoder.createDataInstance();
			this._current_data=i_encoder.createDataInstance();
			this._encoder=i_encoder;
			return;
		}
		private NyARIntPoint2d[] __tmp_vertex=NyARIntPoint2d.createArray(4);
		/**
		 * Initialize call back handler.
		 */
                public void init(INyARRgbRaster i_raster,INyIdMarkerData i_prev_data)
		{
//			this.marker_data=null;
                        
			this._prev_data=i_prev_data;
			this._ref_raster=i_raster;
                        result_stack.clear();
		}
		private final NyIdMarkerParamEn _marker_param=new NyIdMarkerParamEn();
		private final NyIdMarkerPattern _marker_data=new NyIdMarkerPattern();

		/**
                 * is called each time a rectangle is found.
                 * Inspect the rectangular pattern was found to ensure the data
                 * considering the vertex orientation.
		 */
		public void onSquareDetect(NyARSquareContourDetector i_sender,
                        int[] i_coordx,int[] i_coordy,int i_coor_num,int[] i_vertex_index)
                        throws NyARException
		{
			//Contour coordinates into the vertex list
			NyARIntPoint2d[] vertex=this.__tmp_vertex;
			vertex[0].x=i_coordx[i_vertex_index[0]];
			vertex[0].y=i_coordy[i_vertex_index[0]];
			vertex[1].x=i_coordx[i_vertex_index[1]];
			vertex[1].y=i_coordy[i_vertex_index[1]];
			vertex[2].x=i_coordx[i_vertex_index[2]];
			vertex[2].y=i_coordy[i_vertex_index[2]];
			vertex[3].x=i_coordx[i_vertex_index[3]];
			vertex[3].y=i_coordy[i_vertex_index[3]];
		
			NyIdMarkerParamEn param=this._marker_param;
			NyIdMarkerPattern patt_data  =this._marker_data;			
			// Cut from a pattern image criteria
                        // menghasilkan patt_data / marker pattern dari hasil pick up
			if (!this._id_pickup.pickFromRaster(this._ref_raster,vertex, patt_data, param)){
				return;
			}
			//encoding
                        //bikin nyid data / marker data (hasil encode dari patt_data)
			if(!this._encoder.encode(patt_data,this._data_temp)){
				return;
			}
			//Recognition that is required to continue?
			if (this._prev_data==null){
				//ga diproses pengecekan dengan prev nya
//                                System.out.println("marker nyid IF = "+generateNyid(this._data_temp));
				this._current_data.copyFrom(this._data_temp);
                                
			}
                        else{
				//diproses kesamaan dengan prev nya
				if(!this._prev_data.isEqual((this._data_temp))){

					return;//ID when the request was not the recognition.
				}
			}

                        //kalo multi di bagian ini hasil nya harus disimpen ke dalam stack
                        //datanya = square , (confidence - buat magic symbol) , ID
                         //consistent marker information, the information recorded as a rectangle.

                        final NyARDetectMarkerResult result = this.result_stack.prePush();
                        result.NyID = generateNyid(_current_data);
//                        System.out.println("marker nyid = "+result.NyID);

                        final NyARSquare sq=result.square;
			// newly recognized only when there is a continued or
                        // renewed during recognition, Square to update the information.
                        // from here which is not run only in this condition.
			//Considering the direction, square update
			for(int i=0;i<4;i++){
				int idx=(i+4 - param.direction) % 4;
				this._coordline.coord2Line(i_vertex_index[idx],i_vertex_index[(idx+1)%4],i_coordx,i_coordy,i_coor_num,sq.line[i]);
			}
			for (int i = 0; i < 4; i++) {
				//Calculate intersection between line
				if(!NyARLinear.crossPos(sq.line[i],sq.line[(i + 3) % 4],sq.sqvertex[i])){
					throw new NyARException();//If you double-buffered OK to return this error
				}
			}

//                        for (int i = 0; i < this.result_stack.getLength(); i++) {    // ngecek isi dari stack
//                            System.out.println("detect "+i+" = "+result_stack.getItem(i).NyID);
//                          }
//
		}
	}	
	/**
	 * The owner is free to use the variable tag.
	 */
	public Object tag;

        private static final int max = 300;
	private NyARSquareContourDetector_Rle _square_detect;
	protected INyARTransMat _transmat;
	private NyARRectOffset[] _offset;
	private boolean _is_active;
	private int _current_threshold=110;
	// [AR] for storing the detection result
	private NyARBinRaster _bin_raster;
	private NyARRasterFilter_ARToolkitThreshold _tobin_filter;
	private DetectSquareCB _callback;
	private INyIdMarkerData _data_current;

        public MarkerDetectorNyID(NyARParam i_param,INyIdMarkerDataEncoder i_encoder,
                double[] i_marker_width,int i_raster_format, int i_num_nyid) throws NyARException
	{   
                initInstance(i_param, i_encoder, i_marker_width, i_raster_format, i_num_nyid);
		return;
	}
	private boolean _initialized=false;

        private void initInstance(NyARParam i_param,INyIdMarkerDataEncoder i_encoder,
                double[] i_marker_width,int i_raster_format, int i_num_nyid)
                throws NyARException
	{
		//Initialized?

		assert(this._initialized==false);
		
		NyARIntSize scr_size = i_param.getScreenSize();
		// Analysis to make the object
		this._square_detect = new NyARSquareContourDetector_Rle(scr_size);
		this._transmat = new NyARTransMat(i_param);
		this._callback=new DetectSquareCB(i_param,i_encoder);

		// Create a binary image buffer

		this._bin_raster = new NyARBinRaster(scr_size.w, scr_size.h);
		//Data for two objects to create work
		this._data_current=i_encoder.createDataInstance();
		this._tobin_filter =new NyARRasterFilter_ARToolkitThreshold(110,i_raster_format);
		this._threshold_detect=new NyARRasterThresholdAnalyzer_SlidePTile(15,i_raster_format,4);
		this._initialized=true;
		this._is_active=false;
//		this._offset=new NyARRectOffset();
//		this._offset.setSquare(i_marker_width);
		this._offset = NyARRectOffset.createArray(i_num_nyid);
		for(int i=0;i<i_num_nyid;i++){
			this._offset[i].setSquare(i_marker_width[i]);
		}
		return;		
	}


        public int detectMarkerLite(INyARRgbRaster i_raster) throws NyARException
	{
		// Check size

		if (!this._bin_raster.getSize().isEqualSize(i_raster.getSize().w, i_raster.getSize().h)) {
			throw new NyARException();
		}
		// To convert a binary raster image.

		this._tobin_filter.setThreshold(this._current_threshold);
		this._tobin_filter.doFilter(i_raster, this._bin_raster);

		// Find square code (the second marker argument, or find a new marker.)

                this._callback.init(i_raster,this._is_active?this._data_current:null);
//                System.out.println("------- dalam detectmarker lite-------------");
		this._square_detect.detectMarkerCB(this._bin_raster, this._callback);

		// Recognition status update (if you have discovered a marker, current_data passing Kanji)

		//Feedback threshold (detectExistMarker I have too)

//                System.out.println("------- keluar detectmarker lite-------------");
		return this._callback.result_stack.getLength();

	}
	
	private NyARRasterThresholdAnalyzer_SlidePTile _threshold_detect;
	private NyARTransMatResult __NyARSquare_result = new NyARTransMatResult();

        public int generateNyid(INyIdMarkerData i_code){
        NyIdMarkerData_RawBit code = (NyIdMarkerData_RawBit)i_code;
        int current_id=0;
                if (code.length > 4)
                {
                    //At least 4 characters do not convert an int.
                    current_id = -1;//undefined_id
                }
                else
                {
                    current_id = 0;
                    //And connecting up to four one-byte conversion int value
                    for (int i = 0; i < code.length; i++)
                    {
                        current_id = (current_id << 8) | code.packet[i];
                    }
                }
                return current_id;
        }

	/**
         * * I_index markers to calculate the transformation matrix, the resulting value is placed into the o_result. DetectMarkerLite not use previously executed and not successful.
        *
        * @ Param i_index
        * The index number of the marker. DetectMarkerLite just performed and must be less than the return value of 0.
        * @ Param o_result
        * Please specify an object to receive the results.
        * @ Throws NyARException
	 */
	public void getTransmationMatrix(int i_index, NyARTransMatResult o_result) throws NyARException
	{
		final NyARDetectMarkerResult result = this._callback.result_stack.getItem(i_index);
		// 一番一致したマーカーの位置とかその辺を計算 Calculate the area or location of the most consistent marker
                if (result.NyID != 0){
//                        System.out.println(i_index+" --- "+result.NyID);
			_transmat.transMat(result.square, this._offset[result.NyID-1], o_result);
            }else{
                    System.out.println("error di get transmatrix");
            }
		return;
	}

	/**
         * * I_index ARCode returns the index markers.
        *
        * @ Param i_index
        * The index number of the marker. DetectMarkerLite just performed and must be less than the return value of 0.
        * @ Return
	 */
	public int getNyID(int i_index)
	{
		return this._callback.result_stack.getItem(i_index).NyID;
	}
}
